/* SPU Parallel Elliptic Curve Library - Provides Elliptic Curve Cryptographic
 * functions to the Synergistic Processing Unit target platform
 *
 * Copyright (C) 2008  Michael Annichiarico (mike.gcdeveloper@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef FIELD2M_H
#define FIELD2M_H

#include <spu_intrinsics.h>
//#include<libmpm.h>

#include<stdio.h> /* debugging */

#ifndef FIELDINLINE
#define FIELDINLINE
#endif

#define doal16 __attribute__((aligned (16)))

typedef unsigned long long VECTOROF;
typedef vec_ullong2 doal16 ELEMENT;

/*
#define WORDSIZE	(sizeof(ELEMENT)*8)
#define NUMBITS		127
//#define NUMBITS		153
//#define NUMBITS		63

#define	NUMWORD(_n)		((_n)/WORDSIZE)
#define UPRSHIFT(_n)	((_n)%WORDSIZE)

#define MAXLONG		(NUMWORD+1)

#define MAXBITS		(MAXLONG*WORDSIZE)
#define MAXSHIFT	(WORDSIZE-1)
#define MSB			(1LL<<MAXSHIFT)

#define UPRBIT		(1LL<<(UPRSHIFT-1))
#define UPRMASK		(~(-1LL<<UPRSHIFT))
#define SUMLOOP(i)	for(i=0; i<MAXLONG; i++)

*/

#define ELEMENTBITS	(sizeof(ELEMENT)*8) /* W */
#define MAXDEGREE	(233)		/* m-1 */
#define MAXDEGREEM	(MAXDEGREE+1)	/* m */
#define MAXWORDS	((MAXDEGREEM+(ELEMENTBITS-1))/ELEMENTBITS) /* t */

#define NUMVECELEMS	(sizeof(ELEMENT)/sizeof(VECTOROF))
#define NUMELEMS	(MAXWORDS*NUMVECELEMS)

#define USELOOPVECTORS	ELEMENT* _v; INDEX _vi;
#define USELOOPVECTOROF	VECTOROF* _e; INDEX _ei;

#define LOOPVECTOROF(_PF2M)	for(_e=_PF2M->e,_ei=0;_ei<(INDEX)NUMELEMS;++_ei)
#define LOOPVECTORS(_PF2M)	for(_v=_PF2M->v,_vi=0;_vi<(INDEX)MAXWORDS;++_vi)
#define LOOPDOWNVECTORS(_PF2M)	for(_v=_PF2M->v,_vi=MAXWORDS-1;_vi>=(INDEX)0;--_vi)
#define LOOPDBLDOWNVECTORS(_PF2M)	for(_v=_PF2M->v,_vi=2*MAXWORDS-1;_vi>=(INDEX)0;--_vi)
#define LOOPDBLVECTOROF(_PF2M)	for(_e=_PF2M->e,_ei=0;_ei<(INDEX)NUMELEMS*2;++_ei)
#define LOOPDBLVECTORS(_PF2M)	for(_v=_PF2M->v,_vi=0;_vi<(INDEX)MAXWORDS*2;++_vi)

//#define FIELD2M_NOUNROLL
#ifdef FIELD2M_NOUNROLL
#define LOOPVECTOROFx(_PF2M,XXX)	for(_e=_PF2M->e,_ei=0;_ei<(INDEX)NUMELEMS;++_ei) {XXX}
#define LOOPVECTORSx(_PF2M,XXX)	for(_v=_PF2M->v,_vi=0;_vi<(INDEX)MAXWORDS;++_vi) {XXX}
#define LOOPDOWNVECTORSx(_PF2M,XXX)	for(_v=_PF2M->v,_vi=MAXWORDS-1;_vi>=(INDEX)0;--_vi) {XXX}
#define LOOPDBLDOWNVECTORSx(_PF2M,XXX)	for(_v=_PF2M->v,_vi=2*MAXWORDS-1;_vi>=(INDEX)0;--_vi) {XXX}
#define LOOPDBLVECTOROFx(_PF2M,XXX)	for(_e=_PF2M->e,_ei=0;_ei<(INDEX)NUMELEMS*2;++_ei) {XXX}
#define LOOPDBLVECTORSx(_PF2M,XXX)	for(_v=_PF2M->v,_vi=0;_vi<(INDEX)MAXWORDS*2;++_vi) {XXX}
#else
#define LOOPVECTOROFx(_PF2M,XXX)	{_e=_PF2M->e;_ei=0; { XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; }}
#define LOOPVECTORSx(_PF2M,XXX)		{_v=_PF2M->v;_vi=0; { XXX _vi+=1; XXX _vi+=1; }}
#define LOOPDOWNVECTORSx(_PF2M,XXX)	{_v=_PF2M->v;_vi=MAXWORDS-1; { XXX _vi-=1; XXX _vi-=1; }}
#define LOOPDBLDOWNVECTORSx(_PF2M,XXX)	{_v=_PF2M->v;_vi=2*MAXWORDS-1; { XXX _vi-=1; XXX _vi-=1; XXX _vi-=1; XXX _vi-=1; }}
#define LOOPDBLVECTOROFx(_PF2M,XXX)	{_e=_PF2M->e;_ei=0; { XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; XXX _ei+=1; }}
#define LOOPDBLVECTORSx(_PF2M,XXX)	{_v=_PF2M->v;_vi=0; { XXX _vi+=1; XXX _vi+=1; XXX _vi+=1; XXX _vi+=1; }}
#endif

typedef signed short INDEX;

typedef union {
	ELEMENT v[MAXWORDS];
	VECTOROF e[NUMELEMS];
} doal16  FIELD2M;
typedef FIELD2M* PFIELD2M;

typedef union {
	ELEMENT v[MAXWORDS*2];
	VECTOROF e[NUMELEMS*2];
} doal16  DBLFIELD2M;
typedef DBLFIELD2M* PDBLFIELD2M;

#define makeEL (ELEMENT)(vec_ullong2)


unsigned int poly_degree(const PFIELD2M A);
INDEX poly_findtop(PFIELD2M p);
unsigned int poly_degree_be(const PFIELD2M A);
INDEX poly_findtop_be(PFIELD2M p);
void poly_print(PFIELD2M p);
void poly_printnice(PFIELD2M p);
void poly_reduce_nist233(PDBLFIELD2M C);
void poly_add(PFIELD2M C, PFIELD2M A, PFIELD2M B);
void poly_shiftr1(PFIELD2M C, PFIELD2M A);
void poly_shiftl1(PFIELD2M C, PFIELD2M A);
void poly_dblshiftl1(PDBLFIELD2M C, PDBLFIELD2M A);
void poly_dblshift(PDBLFIELD2M C, PDBLFIELD2M A, signed int count);
void poly_shift(PFIELD2M C, PFIELD2M A, signed int count);
void poly_shiftr(PFIELD2M C, PFIELD2M A, unsigned int count);
void poly_dblshiftr(PDBLFIELD2M C, PDBLFIELD2M A, unsigned int count);
void poly_shiftl(PFIELD2M C, PFIELD2M A, unsigned int count);
void poly_dblshiftl(PDBLFIELD2M C, PDBLFIELD2M A, unsigned int count);
void poly_mult(PDBLFIELD2M C, PFIELD2M A, PFIELD2M B);
void poly_sqr(PDBLFIELD2M D, PFIELD2M A);
void poly_mod_mult(PFIELD2M C, PFIELD2M A, PFIELD2M B);
void poly_mod_mult_dbl(PDBLFIELD2M D, PFIELD2M A, PFIELD2M B);
void poly_mod_sqr(PFIELD2M C, PFIELD2M A);
void poly_mod_sqr_dbl(PDBLFIELD2M D, PFIELD2M A);
void poly_inv_mod(PFIELD2M U, PFIELD2M A);


extern FIELD2M poly_nist233;
extern FIELD2M poly_zero;
extern FIELD2M poly_one;

#define field2m_init_ull4(pa,pb,pc,pd) \
	{ \
		.v = { \
			[0]=(ELEMENT){0x##pc##ull,0x##pd##ull}, \
			[1]=(ELEMENT){0x##pa##ull,0x##pb##ull} \
		} \
	}
#define field2m_init_ull4be(pa,pb,pc,pd) \
	{ \
		.v = { \
			[0]=(ELEMENT){0x##pa##ull,0x##pb##ull}, \
			[1]=(ELEMENT){0x##pc##ull,0x##pd##ull} \
		} \
	}
#define field2m_init_ul8(pa,pb,pc,pd,pe,pf,pg,ph) \
	field2m_init_ull4( pa##pb , pc##pd , pe##pf , pg##ph )

#define field2m_init_ul8be(pa,pb,pc,pd,pe,pf,pg,ph) \
	field2m_init_ull4be( pa##pb , pc##pd , pe##pf , pg##ph )

#define poly_const_zero field2m_init_ull4(0,0,0,0)
#define poly_const_one field2m_init_ull4(0,0,0,1)

#ifdef VECEQ_UNSUPPORTED
#define vector_cmpeq(a,b) spu_extract(spu_cmpeq(spu_gather(spu_cmpeq((vec_uint4)a,(vec_uint4)b)),15),0)
#elif defined(VECEQ_SUPPORTED)
#define vector_cmpeq(a,b) a==b
#else
#error Must define either VECEQ_SUPPORTED or VECEQ_UNSUPPORTED
#endif

#endif

